package com.guozcc.gateway.exception;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSONObject;
import com.guozcc.gateway.exception.detail.ArgumentCheckException;
import com.guozcc.gateway.exception.detail.GatewayServerException;
import com.guozcc.gateway.utils.GatewayStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.*;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

/**
 * 全局处理异常具体实现类
 */
public class CustomErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    private static final String STATUS = "status";

    //获取SpringBoot上下文
    private final ApplicationContext applicationContext;

    public CustomErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ErrorProperties errorProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
        this.applicationContext = applicationContext;
    }

    /**
     * 获取异常属性，处理异常并封装返回数据
     *
     * @param request
     * @param includeStackTrace
     * @return
     */
    @Override
    protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
        Throwable error = super.getError(request);

        log.info( "晋异常处理类");
        log.error("", error);
        if (error instanceof ArgumentCheckException) {
            //对报文相关字段进行校验，请求参数异常处理
            Map<String, Object> errorAttributes = handleErrorResponse(request,HttpStatus.OK.value(),
                    ((ArgumentCheckException) error).getErrorCode(), ((ArgumentCheckException) error).getErrorMessage());
            return errorAttributes;
        }else if (error instanceof GatewayServerException) {
            //网关自身异常，目前配置白名单
            Map<String, Object> errorAttributes = handleErrorResponse(request,HttpStatus.OK.value(),
                    ((GatewayServerException) error).getErrorCode(), ((GatewayServerException) error).getErrorMessage());
            return errorAttributes;
        } else if (error instanceof NullPointerException) {
            //请求数据为空时
            Map<String, Object> errorAttributes = handleErrorResponse(request,HttpStatus.OK.value(),
                    GatewayStatus.REQUEST_BODY_NULL_EXCEPTION.errcode(), GatewayStatus.REQUEST_BODY_NULL_EXCEPTION.errmsg());
            return errorAttributes;
        } else if (error instanceof NotFoundException) {
            //路由转发服务异常
            Map<String, Object> errorAttributes = handleErrorResponse(request,HttpStatus.OK.value(),
                    GatewayStatus.FORWORD_ROUTE_EXCEPTION.errcode(), GatewayStatus.FORWORD_ROUTE_EXCEPTION.errmsg());
            return errorAttributes;
        } else if (error instanceof ResponseStatusException) {
            //路由转发服务异常
            Map<String, Object> errorAttributes = handleErrorResponse(request,HttpStatus.OK.value(),
                    GatewayStatus.REQUEST_TRADECODE_EXCEPTION.errcode(), GatewayStatus.REQUEST_TRADECODE_EXCEPTION.errmsg());
            return errorAttributes;
        }else if(error instanceof BlockException){
            //兜底异常信息
            Map<String, Object> errorAttributes = handleErrorResponse(request,HttpStatus.OK.value(),
                    GatewayStatus.SENTINEL_EXCEPTION.errcode(), GatewayStatus.SENTINEL_EXCEPTION.errmsg());
            return errorAttributes;
        }else if (error instanceof Exception) {
            //兜底异常信息
            Map<String, Object> errorAttributes = handleErrorResponse(request,HttpStatus.OK.value(),
                    GatewayStatus.DEFAULT_EXCEPTION.errcode(), GatewayStatus.DEFAULT_EXCEPTION.errmsg());
            return errorAttributes;
        } else {
            return super.getErrorAttributes(request, includeStackTrace);
        }
    }

    /***
     * 指定返回数据为JSON格式
     * @param errorAttributes
     * @return
     */
    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }


    /***
     * 统一异常返回处理
     * @param httpResponseStatus
     * @param errorCode
     * @param errorMessage
     * @return
     */
    private Map<String, Object> handleErrorResponse(ServerRequest request,int httpResponseStatus, String errorCode, String errorMessage) {

        //封装返回报文
        Map<String, Object> map = new HashMap<>();
        String tradeCode = request.exchange().getAttribute("tradeCode");
        map.put("head", new JSONObject() {
            {
                put("errcode", errorCode);
                put("errmsg", errorMessage);
            }
        });
        map.put("data", new JSONObject());
//        map.put(STATUS, httpResponseStatus);
        return map;
    }

    /**
     * 清除报文投递过程中的属性信息
     * @param exchange
     */
    private void delectAttributes(ServerWebExchange exchange){

        log.info("remove channelCode:{},tradeCode:{},requestStartTime:{},requestBody:{},responseBody:{}   "+
                exchange.getAttributes().get("channelCode")+"&"+
                exchange.getAttributes().get("tradeCode")+"&"+
                exchange.getAttributes().get("requestStartTime")+"&"+
                exchange.getAttributes().get("requestBody")+"&"+
                exchange.getAttributes().get("responseBody"));

        exchange.getAttributes().remove("requestStartTime");
        exchange.getAttributes().remove("requestBody");
        exchange.getAttributes().remove("responseBody");
        exchange.getAttributes().remove("tradeCode");
        exchange.getAttributes().remove("channelCode");
    }

    /**
     * 自定义异常返回报文，对异常返回报文进行加密处理
     * @param request
     * @return
     */
    @Override
    protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        boolean includeStackTrace = this.isIncludeStackTrace(request, MediaType.ALL);
        Map<String, Object> error = this.getErrorAttributes(request, includeStackTrace);
        String errorMsg = JSONObject.toJSONString(error);
        log.info("异常信息返回："+errorMsg);
        ServerWebExchange exchange = request.exchange();
        return ServerResponse.status(HttpStatus.BAD_REQUEST.value()).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(errorMsg));
    }

}
